#include "stdafx.h"
#include <stdlib.h>
#include <cmath>

#include <gl\glew.h>
#include <gl\glut.h>
#include <gl\GLAUX.H>

#include <string>
#include <set>
using namespace std;

#pragma comment(lib,"OpenGL32.lib")
#pragma comment(lib,"glew32.lib")
#pragma comment(lib,"GLu32.lib")
#pragma comment(lib,"glaux.lib")

#include "vector3d.h"
#include "tools.h"
#include "model_factory.h"
#include "corner_point.h"
#include "path_style.h"
#include "path_component.h"
#include "constant_surface.h"
#include "constant_side.h"
#include "interval_fence.h"
#include "connector_fence.h"
#include "interval_stack.h"
#include "interval_model.h"
#include "obj\glm.h"


#define GRIDSIZE 0.8f
#define HEIGHT_SCALE  0.02f
#define SEA_LEVEL 0.0f
#define EPTHLON 0.00001f
#define DELTA_T 0.1
#define ROAD_WIDTH 0.3
#define CORNER_RADIUS 0.1

void paintWindow();
void reshape(int,int);
void idle();
bool initialize();
bool initShader();
void printInfoLog(GLhandleARB obj);
AUX_RGBImageRec* LoadBMP(char *Filename);
GLint LoadTexture();
GLint LoadTerrain();
void genSkyBox(GLfloat halfLength);
void drawSkyBox(GLfloat halfLength);
void getNormal(int i, int j, AUX_RGBImageRec* terrain, GLfloat* result);
void keyboard(unsigned char key, int x, int y);
void calculatePaths();
void calculatePath(float, float, float, float);
void paintPath();
void init_path_style();
void init_contour(int start_point, float speed);
void contour();
float float_mod(float num, float mod);

inline GLfloat getHeightAtIndex(int x, int z);
GLfloat getHeightAtPostion(GLfloat x, GLfloat z);
GLhandleARB myFragmentShader;
GLhandleARB myVertexShader;
GLhandleARB myProgram;

AUX_RGBImageRec* terrain;
GLuint terrainModel;
GLuint texture[1];
GLuint skyBoxModel;
GLuint skyBoxTexture[6];

vector3d camera_position;

GLfloat player_position[3];
GLfloat player_rise = 0.2;
GLfloat player_look_at[3];

GLfloat playerX = 1.0f;
GLfloat playerZ = 1.0f;
GLfloat playerOrient = 0.0f;
GLfloat target_player_orient = 0.0f;

float round_up(float num, float ratio);

//path
float path_points_list[1024][3];
int point_count = 0;

path_style g_path_style;

set<int> key_point_set;

float target_points[1024][2] = {{3.0f,3.0f},{10.0f,10.0f},{20.0f,18.0f}};
int target_point_count = 3;

corner_point corner_point_list[1024];
int corner_point_num = 0;

GLuint path_texture;

//contour
int cur_start_point;
float SPEED = 0.2;

//terrain environment
float **terrain_environment;
int terrain_x;
int terrain_z;
void draw_pixel_on_environment(int x, int y);
void output_environment();
void draw_tree(vector3d& position, float height);
void blur(float **bmp, int size_x, int size_y);
void load_model();
//model
GLMmodel* p_model;

int _tmain(int argc, _TCHAR* argv[])
{
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800,600);
	glutInitWindowPosition(0,0);
	glutCreateWindow(argv[0]);

	initialize();

	//register functions
	glutDisplayFunc(paintWindow);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutIdleFunc(idle);
	
	//start
	glutMainLoop();

	//release resource
	return 0;
}

bool initialize(){
	glClearColor(0,0,0,0);
	glShadeModel(GL_SMOOTH);
	LoadTexture();
	LoadTerrain();
	init_path_style();
	calculatePaths();
	init_contour(0,SPEED);
	load_model();
	
	//initShader();

	//light
	GLfloat light_position[] = {3.0f, 4.0f, 5.0f, 1.0f};
	GLfloat light_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	//texture
	glEnable(GL_TEXTURE_2D);

	//other
	glEnable(GL_DEPTH_TEST);
	
	return true;
}

void keyboard(unsigned char key, int x, int y)
{
	GLfloat right[3];
	right[0] = cos(playerOrient);
	right[1] = 0.0f;
	right[2] = sin(playerOrient);

	GLfloat front[3];
	front[0] = right[2];
	front[1] = 0.0f;
	front[2] = -right[0];

	GLfloat speed = 0.1;

	switch(key)
	{
	case 'z':
	case 'Z':
		SPEED = 0.0f;
		break;
	case 'x':
	case 'X':
		SPEED += 0.1f;
		break;
	case 'a':
	case 'A':
		player_position[0] += -right[0]*speed;
		player_position[2] += -right[2]*speed;
		break;
	case 'd':
	case 'D':
		player_position[0] += right[0]*speed;
		player_position[2] += right[2]*speed;
		break;
	case 'w':
	case 'W':
		player_position[0] += front[0]*speed;
		player_position[2] += front[2]*speed;
		break;
	case 'S':
	case 's':
		player_position[0] += -front[0]*speed;
		player_position[2] += -front[2]*speed;
		break;
	case 'q':
	case 'Q':
		playerOrient -= 0.05f;
		if(playerOrient < 0.0f)
		{
			playerOrient += 6.28f;
		}
		break;
	case 'e':
	case 'E':
		playerOrient += 0.05f;
		if(playerOrient > 6.28f)
		{
			playerOrient -= 6.28f;
		}
		break;
	case 'J':
	case 'j':
		player_rise -= 0.1;
		break;
	case 'K':
	case 'k':
		player_rise += 0.1;
		break;
	}

}

GLint LoadTexture()
{
	AUX_RGBImageRec* TextureImage[1];
	memset(TextureImage,0,sizeof(void *)*1);
	//Load External Texture
	if((TextureImage[0]=LoadBMP("ground.bmp")))
	{

		//Generate map
		glGenTextures(1,&texture[0]);
		glBindTexture(GL_TEXTURE_2D,texture[0]);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);	
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	

		if(TextureImage[0]->data)
		{
			free(TextureImage[0]->data);
		}
		free(TextureImage[0]);
	}
	//Load Path texture
	memset(TextureImage,0,sizeof(void *)*1);
	if((TextureImage[0]=LoadBMP("path.bmp")))
	{

		//Generate map
		glGenTextures(1,&path_texture);
		glBindTexture(GL_TEXTURE_2D,path_texture);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);	
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	

		if(TextureImage[0]->data)
		{
			free(TextureImage[0]->data);
		}
		free(TextureImage[0]);
	}
	//Load SkyBox textures
	int skyBoxNumber = 6;
	int i;
	for(i=0; i<skyBoxNumber; i++)
	{
		char filename[32];
		sprintf(filename,"skybox_%d.bmp",i);
		AUX_RGBImageRec* skyBoxImage;
		memset(&skyBoxImage,0,sizeof(void*)*1);
		if(skyBoxImage = LoadBMP(filename))
		{
			glGenTextures(1, &skyBoxTexture[i]);
			glBindTexture(GL_TEXTURE_2D, skyBoxTexture[i]);
			glTexImage2D(GL_TEXTURE_2D, 0, 3, skyBoxImage->sizeX, skyBoxImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, skyBoxImage->data);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);	
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

			if(skyBoxImage->data)
			{
				free(skyBoxImage->data);
			}
			free(skyBoxImage);
		}
	}
	return true;
}

GLint LoadTerrain()
{
	memset(&terrain,0,sizeof(void *)*1);
	//Load Terrain
	if((terrain=LoadBMP("test.bmp")))
	{
		int i,j;
		// terrain environment
		terrain_x = terrain->sizeX;
		terrain_z = terrain->sizeY;
		terrain_environment = new float*[terrain->sizeX];
		for(i=0; i<terrain->sizeX; i++)
		{
			terrain_environment[i] = new float[terrain->sizeY];
			for(j=0; j<terrain->sizeY; j++)
			{
				terrain_environment[i][j] = 0.0f;
			}
		}
		
		// terrain model
		terrainModel = glGenLists(1);
		glNewList(terrainModel, GL_COMPILE);
		//draw terrain
		glBegin(GL_TRIANGLES);
		for(j=0; j<terrain->sizeY - 1; j++)
		{
			for(i=0; i<terrain->sizeX - 1; i++)
			{
				GLfloat leftTopPosX = i * GRIDSIZE;
				GLfloat leftTopPosY = GRIDSIZE * j;

				GLfloat normal3f1[3];
				getNormal(i,j,terrain,normal3f1);
				glNormal3fv(normal3f1);
				glTexCoord2f(i/ (GLfloat)(terrain->sizeX - 1), j/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX, 
							getHeightAtIndex(i,j), 
							leftTopPosY);

				GLfloat normal3f2[3];
				getNormal(i+1,j,terrain,normal3f2);
				glNormal3fv(normal3f2);
				glTexCoord2f((i+1)/ (GLfloat)(terrain->sizeX - 1), j/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX + GRIDSIZE, 
							getHeightAtIndex(i+1,j), 
							leftTopPosY);

				GLfloat normal3f3[3];
				getNormal(i,j+1,terrain,normal3f3);
				glNormal3fv(normal3f3);
				glTexCoord2f(i/ (GLfloat)(terrain->sizeX - 1), (j+1)/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX, 
							getHeightAtIndex(i,j+1), 
							leftTopPosY + GRIDSIZE);

				glNormal3fv(normal3f3);
				glTexCoord2f(i/ (GLfloat)(terrain->sizeX - 1), (j+1)/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX, 
							getHeightAtIndex(i,j+1), 
							leftTopPosY + GRIDSIZE);

				glNormal3fv(normal3f2);
				glTexCoord2f((i+1)/ (GLfloat)(terrain->sizeX - 1), j/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX + GRIDSIZE, 
							getHeightAtIndex(i+1,j),
							leftTopPosY);

				GLfloat normal3f4[3];
				getNormal(i+1,j+1,terrain,normal3f4);
				glNormal3fv(normal3f4);
				glTexCoord2f((i+1)/ (GLfloat)(terrain->sizeX - 1), (j+1)/ (GLfloat)(terrain->sizeY - 1));
				glVertex3f(leftTopPosX + GRIDSIZE,
							getHeightAtIndex(i+1,j+1), 
							leftTopPosY + GRIDSIZE);
			}
		}
		glEnd();
		glEndList();
	}
	return true;
}

inline GLfloat getHeightAtIndex(int x, int z)
{
	return SEA_LEVEL + terrain->data[3 * ( z * terrain->sizeX + x )] * HEIGHT_SCALE;
}

GLfloat getHeightAtPostion(GLfloat x, GLfloat z)
{
	if( x < 0.0f || x > (terrain->sizeX - 1) * GRIDSIZE 
		|| z < 0.0f || z > (terrain->sizeY - 1) * GRIDSIZE)
	{
		return SEA_LEVEL;
	}
	int leftTopI = floor(x / GRIDSIZE);
	int leftTopJ = floor(z / GRIDSIZE);

	GLfloat oddX = x / GRIDSIZE - leftTopI;
	GLfloat oddZ = z / GRIDSIZE - leftTopJ;

	GLfloat a = getHeightAtIndex(leftTopI, leftTopJ);
	GLfloat b = getHeightAtIndex(leftTopI +1, leftTopJ);
	GLfloat c = getHeightAtIndex(leftTopI, leftTopJ +1);
	GLfloat d = getHeightAtIndex(leftTopI +1, leftTopJ +1);

	GLfloat upMidX = (1.0f - oddX) * getHeightAtIndex(leftTopI, leftTopJ) 
					+ oddX * getHeightAtIndex(leftTopI + 1,leftTopJ);
	GLfloat downMidX = (1.0f - oddX) * getHeightAtIndex(leftTopI, leftTopJ + 1) 
					+ oddX * getHeightAtIndex(leftTopI + 1, leftTopJ + 1);

	return (upMidX * (1.0f - oddZ) + oddZ * downMidX);
}

void getNormal(int i, int j, AUX_RGBImageRec* terrain, GLfloat* result)
{
	GLfloat dhx = 0.0f;
	GLfloat dhz = 0.0f; 

	if( i == 0)
	{
		dhx = terrain->data[3 * ( j * terrain->sizeX + i + 1 )] - terrain->data[3 * ( j * terrain->sizeX + i )] ;
	}
	else if( i == terrain->sizeX - 1)
	{
		dhx = terrain->data[3 * ( j * terrain->sizeX + i )] - terrain->data[3 * ( j * terrain->sizeX + i - 1 )];
	}
	else
	{
		dhx = terrain->data[3 * ( j * terrain->sizeX + i + 1 )] - terrain->data[3 * ( j * terrain->sizeX + i - 1 )];
	}
	dhx *= HEIGHT_SCALE;

	if ( j == 0)
	{
		dhz = terrain->data[3 * ( (j+1) * terrain->sizeX + i )] - terrain->data[3 * ( j * terrain->sizeX + i )];
	}
	else if( j == terrain->sizeY - 1)
	{
		dhz = terrain->data[3 * ( j * terrain->sizeX + i )] - terrain->data[3 * ( (j-1) * terrain->sizeX + i )];
	}
	else
	{
		dhz = terrain->data[3 * ( (j+1) * terrain->sizeX + i )] - terrain->data[3 * ( (j-1) * terrain->sizeX + i )];
	}
	dhz *= HEIGHT_SCALE;

	GLfloat normalx = GRIDSIZE * 2 * dhx;
	GLfloat normaly = 4 * GRIDSIZE * GRIDSIZE;
	GLfloat normalz = GRIDSIZE * 2 * dhz;

	GLfloat length = (GLfloat)sqrt((float)(normalz * normalx + normaly*normaly + normalz*normalz));

	result[0] = normalx / length;
	result[1] = normaly / length;
	result[2] = normalz / length;

	return;
}

void paintWindow(){
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPushMatrix();

	//update camera
	camera_position.x(player_position[0]);
	camera_position.y(getHeightAtPostion(player_position[0],player_position[2]) + player_rise);
	camera_position.z(player_position[2]);

	glRotatef(playerOrient * 180 / 3.14, 0.0f, 1.0f, 0.0f);

	//sky boxg
	
	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST);
	drawSkyBox(50.0f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	
	//scene model
	glTranslatef(-camera_position.x(), -camera_position.y(), -camera_position.z());

	//gluLookAt(cameraPosX,cameraPosY,cameraPosZ,3.0f,0.0f,3.0f,0.0f,1.0f,0.0f);
	//glTranslatef(0.0f,0.0f,-3.0f);
	glColor3f(1.0,1.0,1.0);
	//glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glCallList(terrainModel);

	//glDisable(GL_LIGHTING);
	//glDisable(GL_DEPTH_TEST);
	glColor3f(1.0,1.0,1.0);
	//glDisable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, path_texture);
	
	paintPath();

	glPopMatrix();
	glutSwapBuffers();
}

void paintPath()
{
	int i;
	bool previous_point_enable = false;
	float previous_point[3];
	float previous_delta[3];
	bool previous_hotpoint_enable = false;

	//clear phrase
	for(i=0; i<g_path_style.component_count; i++)
	{
		g_path_style.components[i]->clear();
	}

	glColor3f(0.4f,0.4f,0.4f);
	bool has_skip_key_point = false;

	for(i=0; i<point_count-1; ++i)
	{
		//calculate basic vectors

		//towards
		float towards[3]; 
		towards[0] = path_points_list[i+1][0] - path_points_list[i][0];
		towards[1] = path_points_list[i+1][1] - path_points_list[i][1];
		towards[2] = path_points_list[i+1][2] - path_points_list[i][2];

		//delta, the bias side points 
		float delta[3];		 
		if(towards[0]*towards[0] < EPTHLON )
		{
			delta[2] = 0.0f;
			delta[1] = 0.0f;
			delta[0] = ROAD_WIDTH;
		}
		else
		{
			delta[2] = ROAD_WIDTH / (sqrt(1+(towards[2]*towards[2])/(towards[0]*towards[0])));
			delta[1] = 0.0f;
			delta[0] = - towards[2] * delta[2] / towards[0];
		}
		if(towards[2]*delta[0] - delta[2]*towards[0] < 0)	// make sure delta x towards > 0, that is, delta 
				// on the right side of towards  
		{
			delta[0] = - delta[0];
			delta[1] = - delta[1];
			delta[2] = -delta[2];
		}
		
		float path_length;
		path_length = sqrt(towards[0]*towards[0] + towards[1]*towards[1] +towards[2]*towards[2]);
		float fact_path_length = path_length;

		//differentiate the key point
		float start_point[3], end_point[3];
		float skip_threshold = 0.0f;
		
		//new
		float start_dist = 0.0f;
		float end_dist = 0.0f;

		//locate starting point
		start_point[0] = path_points_list[i][0];
		start_point[1] = path_points_list[i][1];
		start_point[2] = path_points_list[i][2];
		if(key_point_set.find(i) != key_point_set.end())	//is key point
		{
			float mark_point[3] = {start_point[0], start_point[1] + 1.0f, start_point[2]};
		
			start_point[0] += CORNER_RADIUS*towards[0]/path_length;
			start_point[1] += CORNER_RADIUS*towards[1]/path_length;
			start_point[2] +=  CORNER_RADIUS*towards[2]/path_length;
			skip_threshold += CORNER_RADIUS;
			fact_path_length -= CORNER_RADIUS;

			start_dist += 2*CORNER_RADIUS;
			//end_dist -= 2*CORNER_RADIUS;
	
		}

		//locate ending point
		end_point[0] = path_points_list[i+1][0];
		end_point[1] = path_points_list[i+1][1];
		end_point[2] = path_points_list[i+1][2];
		//end_dist += path_length;
		if(key_point_set.find(i+1) != key_point_set.end())	//is key point
		{
			end_point[0] -= CORNER_RADIUS*towards[0]/path_length;
			end_point[1] -= CORNER_RADIUS*towards[1]/path_length;
			end_point[2] -= CORNER_RADIUS*towards[2]/path_length;
			skip_threshold += CORNER_RADIUS;
			fact_path_length -= CORNER_RADIUS;
			//end_dist -= 2*CORNER_RADIUS;
		}
		end_dist = start_dist + fact_path_length;

		if(path_length< skip_threshold)
		{
			if(key_point_set.find(i) != key_point_set.end() && i!=0)
			{
				has_skip_key_point = true;
			}
			continue;
		}

		//draw component
		int j;
		for(j=0; j<g_path_style.component_count; j++)
		{
			if(g_path_style.components[j]->p_condition == NULL || 
				g_path_style.components[j]->p_condition->judge())
			{
				g_path_style.components[j]->draw(start_point, end_point, start_dist, end_dist, delta);
			}	
		}

		//corner
		if(previous_point_enable && ((i!=0 && key_point_set.find(i)!=key_point_set.end()) || has_skip_key_point))
		{
			has_skip_key_point = false;

			/*printf("start_point> %f,%f,%f\n",start_point[0],start_point[1],start_point[2]);
			printf("start_delta> %f,%f,%f\n",delta[0], delta[1], delta[2]);
			printf("pre_point> %f,%f,%f\n", previous_point[0], previous_point[1], previous_point[2]);
			printf("pre_delta> %f,%f,%f\n", previous_delta[0], previous_delta[1], previous_delta[2]);*/

			for(j=0; j<g_path_style.component_count; j++)
			{
				if(g_path_style.components[j]->p_condition == NULL || 
					g_path_style.components[j]->p_condition->judge())
				{
					g_path_style.components[j]->draw_corner(previous_point, start_point,
						previous_delta, delta,
						path_points_list[i], CORNER_RADIUS);
				}
			}
		}

		//update phrase
		for(j=0; j<g_path_style.component_count; j++)
		{
			g_path_style.components[j]->update_phrase(end_dist);
		}

		previous_point[0] = end_point[0];
		previous_point[1] = end_point[1];
		previous_point[2] = end_point[2];

		previous_delta[0] = delta[0];
		previous_delta[1] = delta[1];
		previous_delta[2] = delta[2];

		previous_point_enable = true;
	}
	glColor3f(1.0,1.0,1.0);
}
void genSkyBox(GLfloat halfLength)
{
	skyBoxModel = glGenLists(1);
	glNewList(skyBoxModel, GL_COMPILE);

	glBegin(GL_QUADS);

	//front
	glVertex3f(-halfLength,halfLength,-halfLength);
	glVertex3f(halfLength,halfLength,-halfLength);
	glVertex3f(halfLength,-halfLength,-halfLength);
	glVertex3f(-halfLength,-halfLength,-halfLength);

	//right
	glVertex3f(halfLength,halfLength,-halfLength);
	glVertex3f(halfLength,halfLength,halfLength);
	glVertex3f(halfLength,-halfLength,halfLength);
	glVertex3f(halfLength,-halfLength,-halfLength);

	//left
	glVertex3f(-halfLength,halfLength,halfLength);
	glVertex3f(-halfLength,halfLength,-halfLength);
	glVertex3f(-halfLength,-halfLength,-halfLength);
	glVertex3f(-halfLength,-halfLength,halfLength);

	//back
	glVertex3f(halfLength,halfLength,halfLength);
	glVertex3f(-halfLength,halfLength,halfLength);
	glVertex3f(-halfLength,-halfLength,halfLength);
	glVertex3f(halfLength,-halfLength,halfLength);

	//top
	glVertex3f(-halfLength,halfLength,halfLength);
	glVertex3f(halfLength,halfLength,halfLength);
	glVertex3f(halfLength,halfLength,-halfLength);
	glVertex3f(-halfLength,halfLength,-halfLength);

	//bottom
	glVertex3f(-halfLength,-halfLength,-halfLength);
	glVertex3f(halfLength,-halfLength,-halfLength);
	glVertex3f(halfLength,-halfLength,halfLength);
	glVertex3f(-halfLength,-halfLength,halfLength);

	glEnd();
	glEndList();
}

void drawSkyBox(GLfloat halfLength)
{
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[0]);
	glBegin(GL_QUADS);
	//front
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(-halfLength,halfLength,-halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(halfLength,halfLength,-halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(halfLength,-halfLength,-halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(-halfLength,-halfLength,-halfLength);
	glEnd();

	//right
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[1]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(halfLength,halfLength,-halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(halfLength,-halfLength,halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(halfLength,-halfLength,-halfLength);
	glEnd();

	//left
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[2]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(-halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(-halfLength,halfLength,-halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(-halfLength,-halfLength,-halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(-halfLength,-halfLength,halfLength);
	glEnd();

	//back
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[3]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(-halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(-halfLength,-halfLength,halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(halfLength,-halfLength,halfLength);
	glEnd();

	//top
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[4]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(-halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(halfLength,halfLength,halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(halfLength,halfLength,-halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(-halfLength,halfLength,-halfLength);
	glEnd();

	//bottom
	glBindTexture(GL_TEXTURE_2D,skyBoxTexture[5]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,1.0f);
	glVertex3f(-halfLength,-halfLength,-halfLength);
	glTexCoord2f(1.0f,1.0f);
	glVertex3f(halfLength,-halfLength,-halfLength);
	glTexCoord2f(1.0f,0.0f);
	glVertex3f(halfLength,-halfLength,halfLength);
	glTexCoord2f(0.0f,0.0f);
	glVertex3f(-halfLength,-halfLength,halfLength);
	glEnd();
}

void reshape(int _width, int _height){
	glViewport(0,0,(GLsizei)_width,(GLsizei)_height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(50,(GLfloat)_width/(GLfloat)_height,0.1f,100.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void idle(){
	contour();
	glutPostRedisplay();
}


bool initShader(){
	printf("initialize shader...\n");
	glewInit();

	FILE* sourceFile;
	char fragmentShaderFile[]="fragment.fx";
	char vertexShaderFile[]="vertex.fx";
	GLcharARB* sourceCode=new char[10000];
	GLint sourceCodeLen;
	//vertex shader
	sourceFile=fopen(vertexShaderFile,"r");
	if(!sourceFile){
		printf("Could not open %s\n",vertexShaderFile);
		return false;
	}
	sourceCodeLen=(GLint)fread(sourceCode,1,10000,sourceFile);
	sourceCode[sourceCodeLen]='\0';
	fclose(sourceFile);
	myVertexShader=glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	const GLcharARB* sourceLineVertex=sourceCode;
	glShaderSourceARB(myVertexShader,1,&sourceLineVertex,&sourceCodeLen);
	glCompileShaderARB(myVertexShader);
	printInfoLog(myVertexShader);

	//fragment shader
	sourceFile=fopen(fragmentShaderFile,"r");
	if(!sourceFile){
		printf("Could not open %s\n",fragmentShaderFile);
		return false;
	}
	sourceCodeLen=(GLint)fread(sourceCode,1,10000,sourceFile);
	sourceCode[sourceCodeLen]='\0';
	fclose(sourceFile);

	const GLcharARB* sourceLineFragment=sourceCode;
	myFragmentShader=glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	glShaderSourceARB(myFragmentShader,1,&sourceLineFragment,NULL);
	glCompileShaderARB(myFragmentShader);
	printInfoLog(myFragmentShader);

	//link
	myProgram=glCreateProgramObjectARB();
	glAttachObjectARB(myProgram,myFragmentShader);
	glAttachObjectARB(myProgram,myVertexShader);
	glLinkProgramARB(myProgram);
	
	//set constants
	glUseProgramObjectARB(myProgram);
	glUniform3fARB(glGetUniformLocationARB(myProgram,"LightDirection"), -1.0, -1.0, -1.0);
	glUniform1iARB(glGetUniformLocationARB(myProgram,"tex"),0);
	printf("shader ok\n");
	return true;
}

void printInfoLog(GLhandleARB obj)
{
	int infoLogLength = 0;
	int charsWritten = 0;
	GLcharARB *infoLog;
	
	glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infoLogLength);
	if( infoLogLength>0 )
	{
		infoLog = new GLcharARB[infoLogLength];
		glGetInfoLogARB(obj, infoLogLength, &charsWritten, infoLog);
		printf("InfoLog:\n%s\n\n", infoLog);
		delete infoLog;
	}
}

void insert_point(float x, float z, bool xz, float k, float road_width)
{
	float delta_z = road_width / sqrt(k*k+1);
	float delta_x = k*delta_z;
	if(!xz)
	{
		float temp = x;
		x = z;
		z = temp;

		temp = delta_z;
		delta_z = delta_x;
		delta_x = temp;
	}

	float heightest = getHeightAtPostion(x-delta_x, z+delta_z);
	float cur_height = getHeightAtPostion(x+delta_x, z-delta_z);
	if(cur_height>heightest)
		heightest = cur_height;
	cur_height = getHeightAtPostion(x,z);
	if(cur_height>heightest)
		heightest = cur_height;

	path_points_list[point_count][0] = x;
	path_points_list[point_count][1] = heightest;
	path_points_list[point_count][2] = z;
	point_count++;
}

int x_comparator(const void* a, const void* b)
{
	float a_x = *((float*)a);
	float b_x = *((float*)b);

	if(a_x > b_x)
		return -1;
	if(a_x < b_x)
		return 1;
	return 0;
}

float round_up(float num, float ratio)
{
	int low = (int)(num / ratio);
	if(num - low*ratio > EPTHLON){
		low +=1;
	}
	return low*ratio;
}

void calculatePaths()
{

	int i;
	for(i=1; i<target_point_count; ++i)
	{
		calculatePath(target_points[i-1][0],target_points[i-1][1],
					target_points[i][0], target_points[i][1]);
		integer_bresenham_line(target_points[i-1][0]/GRIDSIZE, target_points[i-1][1]/GRIDSIZE,
					target_points[i][0]/GRIDSIZE, target_points[i][1]/GRIDSIZE,draw_pixel_on_environment);
	}
	blur(terrain_environment, terrain_x, terrain_z);
}

void calculatePath(float startx, float startz, float endx, float endz)
{
	
	bool xz = true;
	int towards = 1;
	float x1=startx,z1=startz,x2=endx,z2=endz;
	if(fabs(endx-startx) < fabs(endz - startz)){
		xz = false;
		x1 = startz;
		z1 = startx;
		x2 = endz;
		z2 = endx;
	}
	//make sure x1 < x2
	if(x1 > x2)
	{
		towards = -1;
	}

	float cross_x;
	float cross_y;
	float temp_x;
	float temp_z;
	float x,z;
	int up_down;
	int index_x,index_z;
	int temp_index;
	float k,b;
	k = (z2 - z1) / (x2 - x1);
	b = z2 - k*x2;
	//push first
	insert_point(x1,z1,xz,k,ROAD_WIDTH);
	//inset key point
	key_point_set.insert(point_count-1);

	//with z-line
	if(fabs(x2-x1) >= fabs(z2-z1))
	{
		up_down = k>0.0? 1:-1;

		index_x = ceil(x1/GRIDSIZE);
		index_z = ceil(z1/GRIDSIZE);
		bool first_time = true;
		//mid points
		while( (towards > 0 && GRIDSIZE*index_x < x2)
			|| (towards < 0 && GRIDSIZE*(index_x-1) > x2))
		{
			if(towards > 0)
				x = GRIDSIZE*index_x;
			else
				x = GRIDSIZE*(index_x-1);
			z = k * x + b;
			temp_index =  ceil(z/GRIDSIZE);
			if(index_z!=temp_index)	// slash and x-line
			{
				//slash-down
				if(!first_time || (x - x1) + (index_z*GRIDSIZE - z1) > GRIDSIZE)
				{
					temp_x = x;
					if(towards > 0 )
					{
						temp_z = GRIDSIZE*(index_z-1);
					}
					else
					{
						temp_z = GRIDSIZE*index_z;
					}
					
					cross_x = (temp_z+temp_x-b)/(k+1);
					cross_y = k*cross_x + b;

					if( up_down == 1 || 
						(towards > 0 && (temp_x - GRIDSIZE < cross_x && cross_x < temp_x)) ||
						(towards < 0 && (temp_x < cross_x && cross_x < temp_x + GRIDSIZE)))
					{
						insert_point(cross_x, cross_y, xz, k, ROAD_WIDTH);
					}
				}
				//x-line
				if(up_down * towards > 0)
				{
					temp_z = GRIDSIZE*(temp_index-1);
				}
				else
				{
					temp_z = GRIDSIZE*temp_index;
				}
				
				temp_x = (temp_z - b)/k;
				insert_point(temp_x,temp_z,xz,k, ROAD_WIDTH);
			}
			//slash up
			if(!first_time || (x - x1) + (temp_index*GRIDSIZE - z1) > GRIDSIZE)
			{
				temp_x = x;
				if(towards > 0)
					temp_z = GRIDSIZE*(temp_index-1);
				else
					temp_z = GRIDSIZE*temp_index;
				cross_x = (temp_z+temp_x-b)/(k+1);
				cross_y = k*cross_x + b;

				if( up_down == 1 || 
					(towards > 0 && (temp_x - GRIDSIZE < cross_x && cross_x < temp_x)) ||
					(towards < 0 && (temp_x < cross_x && cross_x < temp_x + GRIDSIZE)))
				{
					insert_point(cross_x, cross_y, xz, k, ROAD_WIDTH);
				}
			}

			//z-line
			insert_point(x,z,xz,k, ROAD_WIDTH);
			//refresh
			index_x += towards;
			index_z = temp_index;
			first_time = false;
		}
		//push last
		insert_point(x2,z2,xz, k, ROAD_WIDTH);
		//inset key point
		key_point_set.insert(point_count-1);
	}

}

AUX_RGBImageRec *LoadBMP(char *Filename)	//Load external bmp file and return point
{
	FILE *file=NULL;
	if(!Filename)			// If Filename is empty, return NULL
	{
		return NULL;
	}
	file=fopen(Filename,"r");
	if(file)				// If success open file, load and return its data
	{
		fclose(file);
		return auxDIBImageLoad(Filename);
	}
	return NULL;

	
}

void walk_on_line(float* target, float* cur_position, float* t, float speed)
{
	float towards[3] = {target[0]-cur_position[0], target[1]-cur_position[1], target[2]-cur_position[2]};
	float i_towards = (float)sqrt(towards[0]*towards[0] + towards[1]*towards[1] + towards[2]*towards[2]);
	
	if(towards[2] > EPTHLON)
	{
		target_player_orient = 3.14 - atan(towards[0]/towards[2]);
	}
	else if(towards[2]< -EPTHLON)
	{
		target_player_orient = - atan(towards[0]/towards[2]);
	}
	else
	{
		if(towards[0]>EPTHLON)
		{
			target_player_orient = -3.14/2;
		}
		else
		{
			target_player_orient = 3.14/2;
		}
	}

	//playerOrient += (target_player_orient - playerOrient) *0.1;
	//printf("towards[2]>%f, target>%f, cur>%f\n",towards[2],target_player_orient,playerOrient);

	if(i_towards < speed * (*t))
	{
		cur_position[0] = target[0];
		cur_position[1] = target[1];
		cur_position[2] = target[2];
		(*t) = (*t) - i_towards/speed;
		return;
	}

	cur_position[0] += speed * (*t) * towards[0] / i_towards;
	cur_position[1] += speed * (*t) * towards[1] / i_towards;
	cur_position[2] += speed * (*t) * towards[2] / i_towards;
	*t = 0.0f;
	return;
}

void init_contour(int start_point, float speed)
{
	cur_start_point = start_point;
	player_position[0] = path_points_list[cur_start_point][0];
	player_position[1] = path_points_list[cur_start_point][1];
	player_position[2] = path_points_list[cur_start_point][2];
}

void contour()
{
	float t = DELTA_T;
	while( t>EPTHLON)
	{
		int j = (cur_start_point+1)%point_count;
		
		walk_on_line(path_points_list[j],player_position,&t,SPEED);
		//printf("%f,%f\n",player_position[0],player_position[2]);
		//finish this line
		if(t > EPTHLON)
		{
			cur_start_point = j;
		}
	}
}





void init_path_style()
{
	//g_path_style
	g_path_style.rise = 0.01;
	g_path_style.width = 0.3;
	
	//surface
	constant_surface* p_surface = new constant_surface();
	p_surface->texture = path_texture;
	p_surface->cycle = 1.0f;
	p_surface->rise = 0.1f;
	g_path_style.add_component(p_surface);

	//side
	constant_side* p_side = new constant_side();
	p_side->texture = path_texture;
	p_side->cycle = 0.3f;
	p_side->rise = 0.1f;
	g_path_style.add_component(p_side);

	//fence
	/*interval_fence* p_fence = new interval_fence();
	p_fence->cycle = 0.4f;
	p_fence->rise = 0.05f;
	p_fence->width_position_percent = 0.9f;
	g_path_style.add_component(p_fence);*/

	interval_model* p_fence = new interval_model();
	p_fence->model_name = "tree";
	p_fence->cycle = 0.4f;
	p_fence->rise = 0.2f;
	p_fence->width_position_percent = 0.9f;
	p_fence->scale = 0.1f;
	g_path_style.add_component(p_fence);

	//bar
	connector_fence* p_bar = new connector_fence();
	p_bar->cycle = 0.4f;
	g_path_style.add_component(p_bar);

	//stack
	interval_stack* p_stack = new interval_stack();
	p_stack->cycle = 0.3f;
	p_stack->rise = 0.05f;
	p_stack->width_position_percent = 0.8;
	g_path_style.add_component(p_stack);

}

void draw_pixel_on_environment(int x, int y)
{
	terrain_environment[x][y] = 1.0f;
}

void output_environment()
{
	return;
	srand(0);
	int i,j;
	for(i=0; i<terrain_x; i++)
	{
		for(j=0; j<terrain_z; j++)
		{
			if(terrain_environment[i][j] < 0.2 && rand()*10 < RAND_MAX)
			{
				float x = (i + 0.5)*GRIDSIZE;
				float z = (j + 0.5)*GRIDSIZE;
				float y = getHeightAtPostion(x,z);
				draw_tree(vector3d(x,y,z), (0.2 - terrain_environment[i][j])/0.2);
			}
		}
	}
}

void draw_tree(vector3d& position, float height)
{
	//draw_cylinder(position.xyz(), (position+vector3d(0.0f,height,0.0f)).xyz(),
	//				0.1, SECTION_DIRECTION_Y, true);
	glPushMatrix();
	glTranslatef(position.x(), position.y(), position.z());
	glmDraw(p_model, GLM_SMOOTH | GLM_TEXTURE);
	glPopMatrix();
}

float vol[7][7] = {
	{0.00000067,0.00002292,0.00019117,0.00038771,0.00019117,0.00002292,0.00000067},
	{0.00002292,0.00078633,0.00655965,0.01330373,0.00655965,0.00078633,0.00002292},
	{0.00019117,0.00655965,0.05472157,0.11098164,0.05472157,0.00655965,0.00019117},
	{0.00038771,0.01330373,0.11098164,0.22508352,0.11098164,0.01330373,0.00038771},
	{0.00019117,0.00655965,0.05472157,0.11098164,0.05472157,0.00655965,0.00019117},
	{0.00002292,0.00078633,0.00655965,0.01330373,0.00655965,0.00078633,0.00002292},
	{0.00000067,0.00002292,0.00019117,0.00038771,0.00019117,0.00002292,0.00000067} };


void blur(float **bmp, int size_x, int size_y)
{
	int i,j, n,m;
	float value;
	for(i=3; i< size_x - 3; i++)
	{
		for(j=3; j<size_y-3; j++)
		{
			value = 0.0;
			for(n=0; n<7; n++)
			{
				for(m=0; m<7; m++)
				{
					value += bmp[i-3+n][j-3+m] * vol[n][m];
				}
			}
			bmp[i][j] = value;
		}
	}
}

void load_model()
{
	p_model = glmReadOBJ("tree.obj");
	glmUnitize(p_model);

	model_factory::get_model_factory().load_model(string("tree.obj"),string("tree"));
}